﻿<!DOCTYPE HTML>
<html lang="zh">
<head>
<title>Sound 函数 | AutoHotkey v2</title>
<meta name="description" content="Usage details regarding the SoundGet and SoundSet functions." />
<meta name="ahk:equiv-v1" content="commands/SoundSet.htm" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link href="../static/theme.css" rel="stylesheet" type="text/css" />
<script src="../static/content.js" type="text/javascript"></script>
<script type="text/javascript">$(function(){0<=window.navigator.userAgent.toLowerCase().indexOf("ucbrowser")&&CaoNiMaDeUc()})</script>
</head>
<body>

<h1>Sound 函数</h1>

<p>适用于:</p>
<ul>
  <li><a href="SoundGetVolume.htm">获取音频设备音量</a> / <a href="SoundSetVolume.htm">设置音频设备音量</a></li>
  <li><a href="SoundGetMute.htm">获取音频设备静音状态</a> / <a href="SoundSetMute.htm">设置音频设备静音</a></li>
  <li><a href="SoundGetName.htm">获取音频设备名称</a></li>
  <li><a href="SoundGetInterface.htm">获取音频设备接口</a></li>
</ul>
<p>其他声音相关的函数:</p>
<ul>
  <li><a href="SoundBeep.htm">声音蜂鸣</a></li>
  <li><a href="SoundPlay.htm">声音播放</a></li>
</ul>

<h2 id="devices">终端设备</h2>
<p>SoundGet 和 SoundSet 函数引用的 "devices" 是 <em>音频终端设备</em>. 单个设备驱动程序或物理设备通常具有多个终端, 例如用于不同类型的输出或输入. 例如:</p>
<table class="info">
  <tr><th>名称</th><th>描述</th></tr>
  <tr><td>扬声器(例如 HD Audio)</td><td>此设备的主模拟输出(在环绕声的情况下使用多个插孔).</td></tr>
  <tr><td>数字输出(例如 HD Audio)</td><td>光纤或同轴数字输出.</td></tr>
  <tr><td>麦克风(例如 HD Audio)</td><td>通过麦克风插孔捕获音频.</td></tr>
  <tr><td>立体声混音(例如 HD Audio)</td><td>捕获任何输出到扬声器终端的音频.</td></tr>
</table>
<p>设备名称通常由终端名称(如 "Speakers") 和括号中的音频驱动程序名称组成. 脚本可以使用全名, 也可以仅使用名称的开头, 如 "Mic" 或 "Microphone". 音频驱动程序具有固定名称, 但是管理员可以随时通过声音控制面板更改终端名称.</p>
<p id="mmsys">声音控制面板列出了设备, 从命令行运行 <code>mmsys.cpl</code>, 或通过运行对话框(<kbd>Win</kbd>+<kbd>R</kbd>) 或 <a href="Run.htm">运行</a> 函数可以打开该面板. 默认情况下, 控制面板只列出启用和插入的设备(如果合适), 但这可以通过右键菜单进行更改. AutoHotkey 检测没有插入的设备, 但不检测禁用的设备.</p>

<h2 id="components">组件</h2>
<p>组件显示在声音设备属性对话框的级别选项卡上.</p>
<img src="../static/sound_levels.png" alt="Levels" />
<p>在本例中, 主控制位于顶部, 其次是前四个组件: Microphone, FrontMic, Line In 和 Side. 所有的控制都有音量和静音控制, 除了第四个组件, 它只有音量.</p>
<p>声音设备的属性对话框可以通过<a href="#mmsys">声音控制面板</a>打开.</p>
<p>音频驱动程序能够显示其他控制, 如低音和高音. 但是, 常见的音频驱动程序往往只具有音量和静音控制, 或者根本没有组件. 音量和静音控制是通过 <a href="SoundGetVolume.htm">获取音频设备音量</a>, <a href="SoundSetVolume.htm">设置音频设备音量</a>, <a href="SoundGetMute.htm">获取音频设备静音状态</a> 和 <a href="SoundSetMute.htm">设置音频设备静音</a> 直接支持的. 所有其他控制仅通过 <a href="SoundGetInterface.htm">获取音频设备接口</a> 和 <a href="ComCall.htm">组件调用</a> 间接支持.</p>

<h3 id="Advanced_Details">详细信息</h3>
<p>组件可以通过 <a href="https://docs.microsoft.com/en-us/windows/win32/coreaudio/devicetopology-api">DeviceTopology API</a> 发现, 该 API 公开 <em>Connectors</em> 和 <em>Subunits</em> 的图表. 上面显示的每个组件都有一个连接器, 而连接器定义了组件的名称. 每个控制(例如音量或静音) 由位于连接器和终端之间的子单元表示. 数据从连接器 "流出" 或流入, 并随着流过每个子单元而改变, 例如调节音量或抑制(静音) 所有声音.</p>
<p>SoundGet 和 SoundSet 函数通过遍历 <em>device topology</em> 图并计算具有给定名称的连接器(或所有未指定名称的连接器) 来标识组件. 一旦找到匹配的连接器, 就可以从最近的连接器开始通过查询图形的该特定分支上的每个子单元来检索控制接口(例如 IAudioVolumeLevel 或 IAudioMute).</p>
<p>应用于多个连接器的子单元被排除在外 - 例如与主音量和静音控制对应的子单元. 如果连接器至少有一个自己的子单元, 即使该子单元不是所请求的类型, 该连接器也会被计数.</p>
<p>实际上, 最终结果的可用的组件和<a href="#components">级别选项卡</a>中列出的是一样的, 并且它们的顺序是相同的. 但是, 这个过程是基于观察, 反复尝试的, 所以可能不是 100% 的准确.</p>

<h2 id="Common_Parameters">通用参数</h2>
<dl>

  <dt id="component">Component</dt>
  <dd>
    <p>类型: <a href="../Concepts.htm#strings">字符串</a>或<a href="../Concepts.htm#numbers">整数</a></p>
    <p>以下之一:</p>
    <ul>
      <li><a href="#components">component</a> 的索引, 其中 1 是第一个组件.</li>
      <li>组件的完整显示名称(不区分大小写).</li>
      <li>如上, 但后跟冒号和整数, 其中 1 是第一个使用该名称的组件. 例如, <code>"Line In:2"</code> 是名称为 "Line In" 的第二个组件. 仅当 <em>Component</em> 可能导致歧义时, 例如多个组件具有相同的名称, 或显示名称为空, 为整数或包含冒号时, 才需要这样做.</li>
      <li>如果省略或为空, 则该函数以主音量/静音控制或由 <a href="https://docs.microsoft.com/en-us/windows/win32/api/mmdeviceapi/nf-mmdeviceapi-immdevice-activate">IMMDevice::Activate</a> 返回的接口为目标.</li>
    </ul>
    <p>如果只指定索引, 则忽略显示名称. 例如, <code>1</code>, <code>"1"</code> 和 <code>":1"</code> 使用第一个组件, 而不管名称如何, 而 <code>""</code> 使用主控制.</p>
    <p>如果声音设备缺少指定的 <em>Component</em>, 则抛出异常.</p>
  </dd>

  <dt id="device">Device</dt>
  <dd>
    <p>类型: <a href="../Concepts.htm#strings">字符串</a>或<a href="../Concepts.htm#numbers">整数</a></p>
    <p>以下之一:</p>
    <ul>
      <li>介于 1 和支持的设备总数之间的数字(整数).</li>
      <li>设备的显示名称; 全名或开头部分(不区分大小写). 例如, <code>"Speakers"</code> 或 <code>"Speakers (Example HD Audio)"</code>.</li>
      <li>如上, 但后跟冒号和整数, 其中 1 是具有匹配名称的第一个设备. 例如, <code>"Speakers:2"</code> 表示名称为 "Speakers" 的第二个设备. 仅当 <em>Device</em> 可能导致歧义时, 例如多个组件具有相同的名称, 或包含冒号时, 才需要这样做.</li>
      <li>如果省略或为空, 则默认为系统的默认播放设备(不一定是设备 1).</li>
    </ul>
    <p><a href="#ExSoundcard">声卡分析脚本</a>可以帮助确定要使用的名称和/或编号.</p>
  </dd>

</dl>

<h2 id="Examples">示例</h2>
<div class="ex" id="ExSoundcard">
<p><a href="#ExSoundcard">#1</a>: 声卡分析. 使用以下脚本发现可用的音频设备和组件名称, 以及每个设备或组件是否支持音量和/或静音控件. 它在一个简单的 ListView 中显示结果. 如果可以检索当前音量和静音设置, 则会显示它们, 但不会实时更新.</p>
<pre filename="soundcard.ahk">
scGui := 界面.新建(, "Sound Components")
scLV := scGui.Add('ListView', "w600 h400"
    , ["Component", "#", "Device", "Volume", "Mute"])

devMap := 映射()

循环
{
    <em>; 对于每一次循环迭代, 尝试获取相应的设备.</em>
    try
        devName := 获取音频设备名称(, dev := 内_循环次数)
    捕获  <em>; 没有更多的设备.</em>
        跳出

    <em>; 如果需要, 使用 ":index" 来限定名称.</em>
    devName := Qualify(devName, devMap, dev)

    <em>; 如有可能, 获取主音量和静音设置.</em>
    vol := mute := ""
    try vol := 四舍五入(获取音频设备音量( , dev), 2)
    try mute := 获取音频设备静音状态( , dev)

    <em>; 仅在至少检索到一项时显示主设置.</em>
    如果 vol != "" || mute != ""
        scLV.Add("", "", dev, devName, vol, mute)

    <em>; 对于每个组件, 首先查询其名称.</em>
    cmpMap := 映射()

    循环
    {
        try
            cmpName := 获取音频设备名称(cmp := 内_循环次数, dev)
        捕获
            跳出
        <em>; 如有可能, 检索该组件的音量和静音设置.</em>
        vol := mute := ""
        try vol := 四舍五入(获取音频设备音量(cmp, dev), 2)
        try mute := 获取音频设备静音状态(cmp, dev)
        <em>; 显示此组件, 即使它不支持音量或静音,
        ; 因为它可能通过 获取音频设备接口() 支持其他控制.</em>
        scLV.Add("", Qualify(cmpName, cmpMap, 内_循环次数), dev, devName, vol, mute)
    }
}

循环 5
    scLV.ModifyCol(内_循环次数, 'AutoHdr Logical')
scGui.显示()

<em>; 如果需要, 使用 ":index" 来限定名称.</em>
Qualify(name, names, overallIndex)
{
    如果 name = ''
        返回 overallIndex
    key := 字符串小写(name)
    index := names.Has(key) ? ++names[key] : (names[key] := 1)
    返回 (index &gt; 1 || 查找(name, ':') || 是否整数(name)) ? name ':' index : name
}</pre>
</div>

</body>
</html>